/*
 * Copyright (c) 2009, David A. Freels Sr.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that
 * the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
 *     * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package net.whippetcode.plugins.intellij.hudson.util;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

/**
 * This class is used by the plug-in to encrypt/decrypt the password used for authentication.
 *
 * @author David A. Freels Sr.
 */
public final class CipherUtil
{
	private static SecretKeySpec keySpec;

	public static void createEncryptionKey(File storage) throws NoSuchAlgorithmException, IOException
	{
		keySpec = null;
		KeyGenerator generator = KeyGenerator.getInstance("AES");
		generator.init(128);

		SecretKey skey = generator.generateKey();
		byte[] raw = skey.getEncoded();

		OutputStream out = new BufferedOutputStream(new FileOutputStream(storage, false));
		out.write(raw);
		out.flush();
		out.close();
	}

	public static SecretKeySpec loadSecretKey() throws NoSuchAlgorithmException, IOException
	{
		if (keySpec != null)
		{
			return keySpec;
		}

		File file = new File(System.getProperty("user.home") + File.separator + ".hudson.key");
		if (!file.exists())
		{
			createEncryptionKey(file);
		}

		byte[] rawKey = IOUtil.readBytes(file.toURL());

		return new SecretKeySpec(rawKey, "AES");
	}

	/**
	 * This method will take the string and perform encryption and then encode the result.
	 *
	 * @param password The string to encrypt.
	 * @return An encrypted/encoded text string.
	 * @throws InvalidKeyException			 Thrown if the key used for encryption is invalid.
	 * @throws NoSuchAlgorithmException	Thrown if the requested algorithm does not exist.
	 * @throws NoSuchPaddingException		This should not be thrown.
	 * @throws BadPaddingException			 This should not be thrown.
	 * @throws IllegalBlockSizeException This should not be thrown.
	 * @throws java.io.IOException			 THrown if there is trouble loading the key.
	 * @see #encode(byte[])
	 */
	public static String encryptPassword(String password) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException, IOException
	{
		SecretKeySpec key = loadSecretKey();
		Cipher cipher = Cipher.getInstance("AES");

		cipher.init(Cipher.ENCRYPT_MODE, key);

		return encode(cipher.doFinal(password.getBytes()));
	}

	public static String decryptPassword(String password) throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException
	{
		byte[] bytes = decode(password);
		SecretKeySpec key = loadSecretKey();
		Cipher cipher = Cipher.getInstance("AES");

		cipher.init(Cipher.DECRYPT_MODE, key);

		return new String(cipher.doFinal(bytes));
	}

	public static String encode(byte[] text)
	{
		return new BASE64Encoder().encode(text);
	}

	public static byte[] decode(String text) throws IOException
	{
		return new BASE64Decoder().decodeBuffer(text);
	}

}
